package com.example.springDemo.config;


import com.example.springDemo.bean.Pet;
import com.example.springDemo.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.util.StreamUtils;
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.util.UrlPathHelper;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 *  代表请求的request（前端给java的信息）
 *  代表响应response（java给前端的信息）
 *
 *  1、Filter是Servlet定义的原生组件。好处，脱离Spring应用也能使用
 * 2. Interceptor 是Spring定义的接口。可以使用Spring的自动装配等功能
 * Filter和Interceptor的执行顺序     过滤前-拦截前-action执行-拦截后-过滤后
 *  Filter和Interceptor的区别
 *      Filter是基于函数回调的，而Interceptor则是基于Java反射的。
 *      Filter依赖于Servlet容器，而Interceptor不依赖于Servlet容器。
 *      Filter对几乎所有的请求起作用，而Interceptor只能对action请求起作用。
 *      Interceptor可以访问Action的上下文，值栈里的对象，而Filter不能。
 *      在action的生命周期里，Interceptor可以被多次调用，而Filter只能在容器初始化时调用一次。
 *
 *  filter过滤器
 *              Filter有如下几个用处。
 * 在HttpServletRequest到达Servlet之前，拦截客户的HttpServletRequest。
 * 根据需要检查HttpServletRequest，也可以修改HttpServletRequest头和数据。
 * 在HttpServletResponse到达客户端之前，拦截HttpServletResponse。
 * 根据需要检查HttpServletResponse，也可以修改HttpServletResponse头和数据。
 *              Filter有如下几个种类。
 * 用户授权的Filter：Filter负责检查用户请求，根据请求过滤用户非法请求。
 * 日志Filter：详细记录某些特殊的用户请求。
 * 负责解码的Filter:包括对非标准编码的请求解码。
 * 能改变XML内容的XSLT Filter等。
 * Filter可以负责拦截多个请求或响应；一个请求或响应也可以被多个Filter拦截。
 *
 * Interceptor拦截器
 *Interceptor介绍
 *      拦截器，在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前，
 *          进行拦截，然后在之前或之后加入某些操作。拦截是AOP的一种实现策略。
 *      在WebWork的中文文档的解释为—拦截器是动态拦截Action调用的对象。
 *          它提供了一种机制可以使开发者可以定义在一个Action执行的前后执行的代码，
 *          也可以在一个Action执行前阻止其执行。同时也提供了一种可以提取Action中可重用的部分的方式。
 *      拦截器将Action共用的行为独立出来，在Action执行前后执行。
 *          这也就是我们所说的AOP，它是分散关注的编程方法，它将通用需求功能从不相关类之中分离出来；
 *          同时，能够共享一个行为，一旦行为发生变化，不必修改很多类，只要修改这个行为就可以。
 *      拦截器将很多功能从我们的Action中独立出来，大量减少了我们Action的代码，独立出来的行为就有很好的重用性。
 *      当你提交对Action(默认是.action结尾的url)的请求时，ServletDispatcher会根据你的请求，
 *          去调度并执行相应的Action。在Action执行之前，调用被Interceptor截取，
 *          Interceptor在Action执行前后执行。
 *
 * 过滤器和拦截器非常相似，但是它们有很大的区别
 * 最简单明了的区别就是过滤器可以修改request，而拦截器不能
 * 过滤器需要在servlet容器中实现，拦截器可以适用于javaEE，javaSE等各种环境
 * 拦截器可以调用IOC容器中的各种依赖，而过滤器不能
 * 过滤器只能在请求的前后使用，而拦截器可以详细到每个方法
 *
 */
@Configuration(proxyBeanMethods = false)
public class WebConfig /*implements WebMvcConfigurer*/ {

    //自定义filter过滤器
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter methodFilter=new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_method");
        return  methodFilter;
    }

/*
    //开启矩阵变量02
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {

                UrlPathHelper urlPathHelper=new UrlPathHelper();
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
        };
    }
*/

    /*//开启矩阵变量01
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        UrlPathHelper urlPathHelper = new UrlPathHelper();
        urlPathHelper.setRemoveSemicolonContent(false);//不移除;后面的内容。矩阵变量功能就可以生效
        configurer.setUrlPathHelper(urlPathHelper);
    }*/

//    WebMvcConfigurer "定制化"springMVC功能  Configurer配置者
    @Bean
    public WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void configurePathMatch(PathMatchConfigurer configurer) {
                UrlPathHelper urlPathHelper = new UrlPathHelper();
                urlPathHelper.setRemoveSemicolonContent(false);
                configurer.setUrlPathHelper(urlPathHelper);
            }
            @Override//添加自定义转换器
            public void addFormatters(FormatterRegistry registry){
                registry.addConverter(new Converter<String, Pet>() {
                    @Override
                    public Pet convert(String source) {
//                        source=source.trim();
                        if(!source.isEmpty()){
                            Pet pet = new Pet();
                            String[] pets = source.split(",");
                            pet.setName(pets[0]);
                            pet.setWeight(Double.valueOf(pets[1]));
                            return pet;
                        }
                        return null;
                    }
                });
            }
            @Override//给容器中添加一个  协议数据
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new longMessageConverters());
            }

            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                Map<String,MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("xml",MediaType.APPLICATION_XML);
                mediaTypes.put("json",MediaType.APPLICATION_JSON);
                mediaTypes.put("x-long",MediaType.parseMediaType("application/x-long"));

                //基于format=     的
                //http://localhost:8080/test/user?format= json/xml
//                parameterStrategy.setParameterName("");//可以自定义这个 format 改成别的
                ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes);//

                //适配头
                HeaderContentNegotiationStrategy contentNegotiationStrategy = new HeaderContentNegotiationStrategy();

                configurer.strategies(Arrays.asList(parameterStrategy,contentNegotiationStrategy));

            }
            //这个是重写   添加一个application/x-long在  ？后面format=    也能访问

        };

    }

}

//自定义MessageConverters  响应头 协议数据
class longMessageConverters implements HttpMessageConverter<User> {

    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(User.class);
    }//能不能读

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(User.class);
    }//能不能写


    /**
     * *服务器要统计所有MessageConverter都能写出哪些内容类型
     *     application/x-long
     */
    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-long");
    }

    @Override
    public User read(Class<? extends User> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        String temp = StreamUtils.copyToString(inputMessage.getBody(),Charset.forName("UTF-8"));
        String[] tempArr = temp.split(";");
        return new User(tempArr[0],Integer.valueOf(tempArr[1]),new Pet(tempArr[2],Double.valueOf(tempArr[2])) );
    }//写入

    @Override
    public void write(User user, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        String data = user.getName()+";"+user.getAge()+";"+user.getPet().getName()+";"+user.getPet().getWeight();
        outputMessage.getBody().write(data.getBytes(StandardCharsets.UTF_8));
    }//写出
}
